﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Kotonoha.Server.Objects.Model;
using Kotonoha.Server.Utils;
using System.Linq;

namespace Kotonoha.Server.Examples {

    public class ExampleInfoHolder {
        public string Example { get; set;}
        public List<Word> Positions { get; set;}
    }

    public static class ExampleFileReader {
        public static IEnumerable<ExampleInfoHolder> read(Stream file, Encoding encoding, ProgressCallback progress) {
            double size = file.Length;
            StreamPositionReader reader = new StreamPositionReader(file, encoding);
            while (!reader.EndOfStream) {
                long position = reader.Position;
                String lineA = reader.ReadLine();
                String lineB = reader.ReadLine();
                if (lineA[0] != '#' || lineB[0] != '#') {
                    yield return new ExampleInfoHolder {
                       Example = lineA,
                       Positions = parseString(lineB.Substring(3)).ToList()
                    };
                }
                if (progress != null) {
                    progress(null, position / size);
                }
            }
        }

        /// <summary>
        /// Matches:
        /// 0 - whole
        /// 1 - word itself
        /// 2 - first brackets content with brackets
        /// 3 - first brackets content w\o brackets
        /// 4 - second...
        /// 5 - meaning number
        /// 6 - is recommended 
        /// </summary>
        private static readonly Regex re = new Regex(@"(\w+)([\({](\w+)[\)}])?(\[(\d+)\])?(~)? ?", RegexOptions.Compiled);

        public static IEnumerable<Word> parseString(string s) {
            MatchCollection matches = re.Matches(s);
            foreach (Match m in matches) {
                string word = m.Groups[1].Value;
                string reading = m.Groups[3].Value;
                string pos = m.Groups[5].Value;
                string rec = m.Groups[6].Value;
                Word w = new Word(word, reading, String.IsNullOrEmpty(pos) ? 1 : Int32.Parse(pos), rec == "~");
                yield return w;
            }
        }

        //其の{その} 家(いえ)[1] は 可也{かなり} ぼろ屋[1]~ になる[1]{になっている}
    }

    public class ExampleIndexCreator {
        private ExamplesIndex index;

        public ExamplesIndex create(Stream file, Encoding encoding, ProgressCallback progress) {
            index = new ExamplesIndex();
            double size = file.Length;
            StreamPositionReader reader = new StreamPositionReader(file, encoding);
            while (!reader.EndOfStream) {
                long position = reader.Position;
                String lineA = reader.ReadLine();
                String lineB = reader.ReadLine();
                if (lineA[0] != '#' || lineB[0] != '#')
                process(position, lineA.Substring(3), lineB.Substring(3));
                if (progress != null) {
                    progress(this, position/size);
                }
            }

            return index;
        }

        private void process(long position, string a, string b) {
            foreach(Word w in ExampleFileReader.parseString(b)) {
                IndexEntry entry = index[w.Content];
                entry.addCase(position, w.Meaning, w.Recommended);
                
                if (String.IsNullOrEmpty(w.Reading)) {
                    entry = index[w.Reading];
                    entry.addCase(position, w.Meaning, w.Recommended);
                }
            }
        }
    }

    public class Word {
        private readonly string word;
        private readonly string reading;
        private readonly int meaning;
        private readonly bool recommended;

        public Word(string word, string reading, int meaning, bool recommended) {
            this.word = word;
            this.reading = reading;
            this.meaning = meaning;
            this.recommended = recommended;
        }


        public string Content {
            get { return word; }
        }

        public string Reading {
            get { return reading; }
        }

        public int Meaning {
            get { return meaning; }
        }

        public bool Recommended {
            get { return recommended; }
        }
    }
}
